﻿using System.Linq;
using FastTests;
using Raven.Client.Documents.Indexes;
using Tests.Infrastructure;
using Xunit;
using Xunit.Abstractions;

namespace SlowTests.MailingList
{
    /// <summary>
    /// Demonstrates that "Id" can't be used in the where clause of a map/reduce query.
    /// Fails with an ArgumentException of:
    /// 
    ///     The field '__document_id' is not indexed, cannot query on fields that are not indexed
    /// 
    /// Passes if you don't use it in a where clause, or if you call the reduce field something other than "Id".
    /// </summary>
    public class RavenDB_859 : RavenTestBase
    {
        public RavenDB_859(ITestOutputHelper output) : base(output)
        {
        }

        private class Foo
        {
            public string Id { get; set; }
            public Bar[] Bars { get; set; }
        }

        private class Bar
        {
            public string Id { get; set; }
            public string Name { get; set; }
        }

        private class OuterResult
        {
            public string Id { get; set; }
            public int Count { get; set; }
        }

        private class TestIndex : AbstractIndexCreationTask<Foo, TestIndex.Result>
        {
            public class Result
            {
                public string Id { get; set; }
                public int Count { get; set; }
            }

            public TestIndex()
            {
                Map = foos => from foo in foos
                              from bar in foo.Bars
                              select new
                              {
                                  bar.Id,
                                  Count = 1
                              };

                Reduce = results => from result in results
                                    group result by result.Id
                                        into g
                                    select new
                                    {
                                        Id = g.Key,
                                        Count = g.Sum(x => x.Count)
                                    };
            }
        }

        [RavenFact(RavenTestCategory.Querying)]
        public void NestedType()
        {
            using (var documentStore = GetDocumentStore())
            {
                documentStore.ExecuteIndex(new TestIndex());

                using (var session = documentStore.OpenSession())
                {
                    var bar1 = new Bar { Name = "A" };
                    var bar2 = new Bar { Name = "B" };
                    var bar3 = new Bar { Name = "C" };

                    session.Store(bar1);
                    session.Store(bar2);
                    session.Store(bar3);

                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });
                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });
                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });
                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });

                    session.SaveChanges();
                }

                using (var session = documentStore.OpenSession())
                {
                    var results = session.Query<TestIndex.Result, TestIndex>()
                                         .Customize(x => x.WaitForNonStaleResults())
                                         .Where(x => x.Id == "bars/1-A")
                                         .ToList();

                    Assert.Equal(1, results.Count);
                    Assert.Equal("bars/1-A", results[0].Id);
                    Assert.Equal(4, results[0].Count);
                }
            }
        }

        [RavenFact(RavenTestCategory.Querying)]
        public void OuterType()
        {
            using (var documentStore = GetDocumentStore())
            {
                documentStore.ExecuteIndex(new TestIndex());

                using (var session = documentStore.OpenSession())
                {
                    var bar1 = new Bar { Name = "A" };
                    var bar2 = new Bar { Name = "B" };
                    var bar3 = new Bar { Name = "C" };

                    session.Store(bar1);
                    session.Store(bar2);
                    session.Store(bar3);

                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });
                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });
                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });
                    session.Store(new Foo { Bars = new[] { bar1, bar2, bar3 } });

                    session.SaveChanges();
                }

                using (var session = documentStore.OpenSession())
                {
                    var results = session.Query<OuterResult, TestIndex>()
                                         .Customize(x => x.WaitForNonStaleResults())
                                         .Where(x => x.Id == "bars/1-A")
                                         .ToList();

                    Assert.Equal(1, results.Count);
                    Assert.Equal("bars/1-A", results[0].Id);
                    Assert.Equal(4, results[0].Count);
                }
            }
        }
    }
}
